#pragma once
#include "sock.hpp"
#include <poll.h>

#define BACKLOG 5
#define DFL_FD -1
#define NUM 64

namespace ns_poll {
    class PollServer {
    private:
        int port;
        int listen_sock;
    public:
        PollServer(int _port) :port(_port) {}

        void InitServer() {
            listen_sock = ns_sock::Sock::Socket();
            ns_sock::Sock::Bind(listen_sock, port);
            ns_sock::Sock::Listen(listen_sock, BACKLOG);
        }

        void Run() {
            std::cout<<"服务器启动"<<std::endl;
            struct pollfd rfds[64];
            for (int i = 0; i < 64; ++i) {
                rfds[i].fd = DFL_FD;
                rfds[i].events = 0;
                rfds[i].revents = 0;
            }
            //把监听套接字添加到rfds
            rfds[0].fd = listen_sock;
            rfds[0].events |= POLLIN;
            rfds[0].revents = 0;
            while (true) {
                switch (poll(rfds, 64, 2000)) {
                case 0:
                    std::cout << "timeout!!!" << std::endl;
                    break;
                case -1:
                    std::cerr << "poll error!" << std::endl;
                    break;
                default:
                    //处理事件
                    HanderEnent(rfds,NUM);
                    break;
                }
            }
        }

    private:
        void HanderEnent(struct pollfd rfds[], int num) {
            //判定fd是否在rfds中
            for (int i = 0; i < num; ++i) {
                //过滤无效fd
                if (rfds[i].fd == DFL_FD) {
                    continue;
                }
                //筛选已经就绪的事件
                if (rfds[i].revents & POLLIN) {
                    if (rfds[i].fd == listen_sock) {
                        //连接事件就绪
                        //获取连接
                        struct sockaddr_in peer;
                        socklen_t len = sizeof(peer);

                        //这里进行accept时，由于连接事件已经就就绪，所以accept不会阻塞
                        int sock = accept(listen_sock, (struct sockaddr*)&peer, &len);
                        if (sock < 0) {
                            std::cerr << "获取连接错误" << std::endl;
                            continue;
                        }
                        //获取到一个新连接
                        //打印客户端信息
                        uint16_t peer_port = htons(peer.sin_port);
                        std::string peer_ip = inet_ntoa(peer.sin_addr);

                        std::cout << "有一个新连接 " << "ip :[" << peer_ip << "]  port: [" << peer_port << "]" << std::endl;

                        if (!AddFdToPollFd(sock, rfds, NUM)) {
                            std::cerr << "添加失败" << std::endl;
                            close(sock);
                        }//end if
                    }//end if
                    else {
                        //读事件就绪
                        char buffer[1024];
                        ssize_t size = recv(rfds[i].fd, buffer, sizeof(buffer) - 1, 0);
                        if (size > 0) {
                            buffer[size] = 0;
                            std::cout << "echo# " << buffer;
                        }
                        else if (size == 0) {
                            std::cout << "客户端断开连接" << std::endl;
                            close(rfds[i].fd);
                            //清除rfds中的文件描述符
                            rfds[i].fd = DFL_FD;
                        }
                        else {
                            std::cerr << "读取出错!!!" << std::endl;
                            close(rfds[i].fd);
                            //清除rfds中的文件描述符
                            rfds[i].fd = DFL_FD;
                        }
                    }
                }//end if
            }//end for
        }
        bool AddFdToPollFd(int sock, struct pollfd rfds[],int num) {
            for (int i = 0; i < num; ++i) {
                if (rfds[i].fd == DFL_FD) {
                    rfds[i].fd = sock;
                    rfds[i].events |= POLLIN;
                    rfds[i].revents = 0;
                    return true;
                }
            }
            return false;
        }
    };
}

